package org.andengine.entity.text.vbo;

import org.andengine.entity.text.Text;
import org.andengine.opengl.font.IFont;
import org.andengine.opengl.font.Letter;
import org.andengine.opengl.vbo.DrawType;
import org.andengine.opengl.vbo.HighPerformanceVertexBufferObject;
import org.andengine.opengl.vbo.VertexBufferObjectManager;
import org.andengine.opengl.vbo.attribute.VertexBufferObjectAttributes;
import org.andengine.util.adt.list.IFloatList;

import java.util.ArrayList;

/**
 * (c) Zynga 2012
 *
 * @author Nicolas Gramlich <ngramlich@zynga.com>
 * @since 12:38:43 - 29.03.2012
 */
public class HighPerformanceTextVertexBufferObject extends HighPerformanceVertexBufferObject implements ITextVertexBufferObject {
    // ===========================================================
    // Constants
    // ===========================================================

    // ===========================================================
    // Fields
    // ===========================================================

    // ===========================================================
    // Constructors
    // ===========================================================

    public HighPerformanceTextVertexBufferObject(final VertexBufferObjectManager pVertexBufferObjectManager, final int pCapacity, final DrawType pDrawType, final boolean pAutoDispose, final VertexBufferObjectAttributes pVertexBufferObjectAttributes) {
        super(pVertexBufferObjectManager, pCapacity, pDrawType, pAutoDispose, pVertexBufferObjectAttributes);
    }

    // ===========================================================
    // Getter & Setter
    // ===========================================================

    // ===========================================================
    // Methods for/from SuperClass/Interfaces
    // ===========================================================

    @Override
    public void onUpdateColor(final Text pText) {
        final float[] bufferData = this.mBufferData;

        final float packedColor = pText.getColor().getABGRPackedFloat();

        int bufferDataOffset = 0;
        final int charactersMaximum = pText.getCharactersMaximum();
        for (int i = 0; i < charactersMaximum; i++) {
            bufferData[bufferDataOffset + 0 * Text.VERTEX_SIZE + Text.COLOR_INDEX] = packedColor;
            bufferData[bufferDataOffset + 1 * Text.VERTEX_SIZE + Text.COLOR_INDEX] = packedColor;
            bufferData[bufferDataOffset + 2 * Text.VERTEX_SIZE + Text.COLOR_INDEX] = packedColor;
            bufferData[bufferDataOffset + 3 * Text.VERTEX_SIZE + Text.COLOR_INDEX] = packedColor;
            bufferData[bufferDataOffset + 4 * Text.VERTEX_SIZE + Text.COLOR_INDEX] = packedColor;
            bufferData[bufferDataOffset + 5 * Text.VERTEX_SIZE + Text.COLOR_INDEX] = packedColor;

            bufferDataOffset += Text.LETTER_SIZE;
        }

        this.setDirtyOnHardware();
    }

    @Override
    public void onUpdateVertices(final Text pText) {
        final float[] bufferData = this.mBufferData;

        // TODO Optimize with field access?
        final IFont font = pText.getFont();
        final ArrayList<CharSequence> lines = pText.getLines();
        final float lineHeight = font.getLineHeight();
        final IFloatList lineWidths = pText.getLineWidths();

        final float lineAlignmentWidth = pText.getLineAlignmentWidth();

        int charactersToDraw = 0;
        int bufferDataOffset = 0;

        final int lineCount = lines.size();
        for (int row = 0; row < lineCount; row++) {
            final CharSequence line = lines.get(row);

            float xBase;
            switch (pText.getHorizontalAlign()) {
                case RIGHT:
                    xBase = lineAlignmentWidth - lineWidths.get(row);
                    break;
                case CENTER:
                    xBase = (lineAlignmentWidth - lineWidths.get(row)) * 0.5f;
                    break;
                case LEFT:
                default:
                    xBase = 0;
            }

            final float yBase = row * (lineHeight + pText.getLeading());

            final int lineLength = line.length();
            Letter previousLetter = null;
            for (int i = 0; i < lineLength; i++) {
                final Letter letter = font.getLetter(line.charAt(i));
                if (previousLetter != null) {
                    xBase += previousLetter.getKerning(letter.mCharacter);
                }

                if (!letter.isWhitespace()) {
                    final float x = xBase + letter.mOffsetX;
                    final float y = yBase + letter.mOffsetY;

                    final float y2 = y + letter.mHeight;
                    final float x2 = x + letter.mWidth;

                    final float u = letter.mU;
                    final float v = letter.mV;
                    final float u2 = letter.mU2;
                    final float v2 = letter.mV2;

                    bufferData[bufferDataOffset + 0 * Text.VERTEX_SIZE + Text.VERTEX_INDEX_X] = x;
                    bufferData[bufferDataOffset + 0 * Text.VERTEX_SIZE + Text.VERTEX_INDEX_Y] = y;
                    bufferData[bufferDataOffset + 0 * Text.VERTEX_SIZE + Text.TEXTURECOORDINATES_INDEX_U] = u;
                    bufferData[bufferDataOffset + 0 * Text.VERTEX_SIZE + Text.TEXTURECOORDINATES_INDEX_V] = v;

                    bufferData[bufferDataOffset + 1 * Text.VERTEX_SIZE + Text.VERTEX_INDEX_X] = x;
                    bufferData[bufferDataOffset + 1 * Text.VERTEX_SIZE + Text.VERTEX_INDEX_Y] = y2;
                    bufferData[bufferDataOffset + 1 * Text.VERTEX_SIZE + Text.TEXTURECOORDINATES_INDEX_U] = u;
                    bufferData[bufferDataOffset + 1 * Text.VERTEX_SIZE + Text.TEXTURECOORDINATES_INDEX_V] = v2;

                    bufferData[bufferDataOffset + 2 * Text.VERTEX_SIZE + Text.VERTEX_INDEX_X] = x2;
                    bufferData[bufferDataOffset + 2 * Text.VERTEX_SIZE + Text.VERTEX_INDEX_Y] = y2;
                    bufferData[bufferDataOffset + 2 * Text.VERTEX_SIZE + Text.TEXTURECOORDINATES_INDEX_U] = u2;
                    bufferData[bufferDataOffset + 2 * Text.VERTEX_SIZE + Text.TEXTURECOORDINATES_INDEX_V] = v2;

                    bufferData[bufferDataOffset + 3 * Text.VERTEX_SIZE + Text.VERTEX_INDEX_X] = x2;
                    bufferData[bufferDataOffset + 3 * Text.VERTEX_SIZE + Text.VERTEX_INDEX_Y] = y2;
                    bufferData[bufferDataOffset + 3 * Text.VERTEX_SIZE + Text.TEXTURECOORDINATES_INDEX_U] = u2;
                    bufferData[bufferDataOffset + 3 * Text.VERTEX_SIZE + Text.TEXTURECOORDINATES_INDEX_V] = v2;

                    bufferData[bufferDataOffset + 4 * Text.VERTEX_SIZE + Text.VERTEX_INDEX_X] = x2;
                    bufferData[bufferDataOffset + 4 * Text.VERTEX_SIZE + Text.VERTEX_INDEX_Y] = y;
                    bufferData[bufferDataOffset + 4 * Text.VERTEX_SIZE + Text.TEXTURECOORDINATES_INDEX_U] = u2;
                    bufferData[bufferDataOffset + 4 * Text.VERTEX_SIZE + Text.TEXTURECOORDINATES_INDEX_V] = v;

                    bufferData[bufferDataOffset + 5 * Text.VERTEX_SIZE + Text.VERTEX_INDEX_X] = x;
                    bufferData[bufferDataOffset + 5 * Text.VERTEX_SIZE + Text.VERTEX_INDEX_Y] = y;
                    bufferData[bufferDataOffset + 5 * Text.VERTEX_SIZE + Text.TEXTURECOORDINATES_INDEX_U] = u;
                    bufferData[bufferDataOffset + 5 * Text.VERTEX_SIZE + Text.TEXTURECOORDINATES_INDEX_V] = v;

                    bufferDataOffset += Text.LETTER_SIZE;
                    charactersToDraw++;
                }

                xBase += letter.mAdvance;

                previousLetter = letter;
            }
        }
        pText.setCharactersToDraw(charactersToDraw);

        this.setDirtyOnHardware();
    }

    // ===========================================================
    // Methods
    // ===========================================================

    // ===========================================================
    // Inner and Anonymous Classes
    // ===========================================================
}